#include "GitServer.h"
#include "QHttpServer/qhttpserver.h"
#include "QHttpServer/qhttprequest.h"
#include "QHttpServer/qhttpresponse.h"
#include "Storage.h"

GitServer::GitServer() : server_(new QHttpServer()), writer_(new Storage())
{

}

GitServer::~GitServer()
{
    server_->close();
}

bool GitServer::startGitServer(const QString &gitProjectsRootPath, int localPort, int extra)
{
    if (!server_->listen(QHostAddress::LocalHost, localPort)) {
        return false;
    }

    static const QString headerContentMark("\r\n\r\n");
    const QString gitRootPath = QFileInfo(gitProjectsRootPath).absoluteFilePath();
    writer_->setRootPath(gitRootPath);
    writer_->setStorage(extra);

    QObject::connect(server_.data(), &QHttpServer::newRequest,
                     this, [&, gitRootPath](QHttpServerRequest *request, QHttpResponse *response) {

        request->storeBody();
        QUrl urlstr = request->url();
        QString pathstr = urlstr.path();

        writer_->watchPath(pathstr); //cyan 监听目录

        HeaderHash reqHeaders = request->headers();
        QByteArray body = request->body();

        bool hasContent = (reqHeaders.contains("content-length") && reqHeaders["content-length"].toInt() != body.size());
        bool isChunked = (reqHeaders.contains("transfer-encoding") && reqHeaders["transfer-encoding"] == "chunked");

        if (hasContent || (isChunked && !request->property("chunked").toBool())) {

            response->writeHead(200);
            QObject::connect(request, &QHttpServerRequest::data,
                             request, [&, request, response] (const QByteArray &chunk) {
                int chunkSize = chunk.size();
                QTimer::singleShot(0, this, [&, request, response, chunkSize] {
                    if (request->headers().contains("content-length")) {
                        int contentLen = request->headers()["content-length"].toInt();
                        if (contentLen < request->body().length()) {
                            return;
                        }
                    } else {
                        if (chunkSize != 20) {
                            return;
                        } else {
                            request->setProperty("chunked", true);
                        }
                    }
                    server_->newRequest(request, response);
                });
            });
            return;
        }


        QStringList env = QProcess::systemEnvironment();
        env.append("GIT_PROJECT_ROOT=" + gitRootPath);
        env.append("GIT_HTTP_EXPORT_ALL=0");
        env.append("REMOTE_USER=$REDIRECT_REMOTE_USER");

        QString dirName = "/" + QDir(gitRootPath).dirName() + "/";
        int indexDir = pathstr.indexOf(dirName);
        QString pathInfo;
        if (indexDir >= 0) {
            pathInfo = pathstr.mid(indexDir + dirName.length());
        }

        env.append("PATH_INFO=/" + pathInfo);
        env.append("CONTENT_TYPE=" + reqHeaders["content-type"]);
        env.append("HTTP_ACCEPT=" + reqHeaders["accept"]);
        env.append("REQUEST_URI=" + request->url().toString());

        switch (request->method()) {
        case QHttpServerRequest::HttpMethod::HTTP_GET:
            env.append("REQUEST_METHOD=GET");
            if (request->url().toString().contains("git-receive-pack"))
                env.append("QUERY_STRING=" + request->url().query());
            break;
        case QHttpServerRequest::HttpMethod::HTTP_POST:
            env.append("REQUEST_METHOD=POST");
            env.append("CONTENT_LENGTH=" + QByteArray::number(request->body().length()));
            env.append("QUERY_STRING=");
            break;
        case QHttpServerRequest::HttpMethod::HTTP_PUT:
            env.append("REQUEST_METHOD=PUT");
            break;
        case QHttpServerRequest::HttpMethod::HTTP_OPTIONS:
            env.append("REQUEST_METHOD=OPTIONS");
            break;
        case QHttpServerRequest::HttpMethod::HTTP_HEAD:
            env.append("REQUEST_METHOD=HEAD");
            break;
        default:
            qCritical() << "unknow method:" << request->method();
            response->writeHead(500);
            response->end();
            return;
        }

        QProcess process;
        process.setEnvironment(env);

        process.start("git-core/git-http-backend", QStringList());
        if (!body.isEmpty()) {
            process.waitForStarted();
            process.write(body);
            process.waitForBytesWritten();
        }
        process.waitForFinished();
        QByteArray ret = process.readAll();
        int index = ret.indexOf(headerContentMark);
        if (index < 0) {
            qCritical() << "program data error.";
            return;
        }

        int statusCode = 200;
        QStringList headers = QString(ret.mid(0, index)).split("\r\n");
        for (QString header : headers) {
            QStringList items = header.split(": ");
            if (items.count() != 2)
                continue;
            QString itemKey = items.at(0).trimmed();
            QString itemValue = items.at(1).trimmed();
            response->setHeader(itemKey, itemValue);
            if (itemKey.toLower() == "status") {
                QStringList status = itemValue.split(" ");
                if (status.count() > 1) {
                    statusCode = status.at(0).toInt();
                    qDebug() << "program code:" << statusCode;
                }
            }
        }

        if (body.isEmpty()) {
            response->writeHead(statusCode);
        }

        QByteArray retData = ret.mid(index + headerContentMark.length());
        response->write(retData);
        response->end();

        writer_->updateFiles(); //cyan git工作完成之后更新
    });

    return true;
}

void GitServer::closeGitServer()
{
    server_->close();
}
